<template>
  <div>
    <!-- 支付信息 -->
    <a-card :loading="loading">
      <!-- 支付信息描述列表 -->
      <a-descriptions :column="3" bordered title="支付信息">
        <a-descriptions-item label="支付单号">{{ payOrder.id }}</a-descriptions-item>
        <a-descriptions-item label="商品标题">{{ payOrder.subject }}</a-descriptions-item>
        <a-descriptions-item label="商品内容">{{ payOrder.body }}</a-descriptions-item>
        <a-descriptions-item label="支付金额">
          {{ payOrder.price | price }}
        </a-descriptions-item>
        <a-descriptions-item label="创建时间">
          {{ payOrder.creationTime | moment }}
        </a-descriptions-item>
        <a-descriptions-item label="过期时间">
          {{ payOrder.expireTime | moment }}
        </a-descriptions-item>
      </a-descriptions>
    </a-card>

    <!-- 支付选择框 -->
    <a-card :loading="submitLoading" style="margin-top: 10px">
      <!-- 支付宝支付渠道 -->
      <a-descriptions title="选择支付宝支付"/>
      <div class="pay-channel-container">
        <div
            v-for="channel in channelsAlipay"
            :key="channel.code"
            class="box"
            @click="submit(channel.code)"
        >
          <img :src="channel.icon"/>
          <div class="title">{{ channel.name }}</div>
        </div>
      </div>
      <!-- 微信支付渠道 -->
      <a-descriptions style="margin-top: 20px" title="选择微信支付"/>
      <div class="pay-channel-container">
        <div
            v-for="channel in channelsWechat"
            :key="channel.code"
            class="box"
            @click="submit(channel.code)"
        >
          <img :src="channel.icon"/>
          <div class="title">{{ channel.name }}</div>
        </div>
      </div>
      <!-- 其它支付渠道 -->
      <a-descriptions style="margin-top: 20px" title="选择其它支付"/>
      <div class="pay-channel-container">
        <div
            v-for="channel in channelsMock"
            :key="channel.code"
            class="box"
            @click="submit(channel.code)"
        >
          <img :src="channel.icon"/>
          <div class="title">{{ channel.name }}</div>
        </div>
      </div>
    </a-card>

    <!-- 条形码支付弹窗 -->
    <a-modal
        v-model="barCode.visible"
        :footer="null"
        :keyboard="false"
        :maskClosable="false"
        :title="barCode.title"
        destroyOnClose
        width="500px"
    >
      <a-form :form="barCodeForm">
        <a-row>
          <a-col :span="24">
            <a-form-item label="条形码" prop="name">
              <a-input v-model="barCode.value" placeholder="请输入条形码"/>
            </a-form-item>
          </a-col>
          <a-col :span="24">
            <div style="text-align: right">
              或使用
              <a href="https://baike.baidu.com/item/条码支付/10711903" style="color: #f5222d;"
                 target="_blank">(扫码枪/扫码盒)</a>
              扫码
            </div>
          </a-col>
        </a-row>
        <div slot="footer" style="text-align:right">
          <a-button
              :disabled="!barCode.value"
              type="primary"
              @click="submit0(barCode.channelCode)"
          >
            确认支付
          </a-button>
          <a-button style="margin-left:8px" @click="barCode.visible = false">取 消</a-button>
        </div>
      </a-form>
    </a-modal>
  </div>
</template>

<script>
/**
 * 支付收银台组件
 * 用于展示支付订单信息，并支持多渠道支付方式选择与提交
 * @author liuxinhh
 */
import * as PayOrderApi from '@/api/pay/order'
import PayChannelCodeEnum from '@/common/enum/pay/PayChannelCodeEnum'
import PayStatusEnum from '@/common/enum/pay/PayStatusEnum'
import PayDisplayModeEnum from '@/common/enum/pay/PayDisplayModeEnum'

import svg_alipay_pc from '@/assets/svgs/pay/icon/alipay_pc.svg'
import svg_alipay_wap from '@/assets/svgs/pay/icon/alipay_wap.svg'
import svg_alipay_app from '@/assets/svgs/pay/icon/alipay_app.svg'
import svg_alipay_qr from '@/assets/svgs/pay/icon/alipay_qr.svg'
import svg_alipay_bar from '@/assets/svgs/pay/icon/alipay_bar.svg'
import svg_wx_pub from '@/assets/svgs/pay/icon/wx_pub.svg'
import svg_wx_lite from '@/assets/svgs/pay/icon/wx_lite.svg'
import svg_wx_app from '@/assets/svgs/pay/icon/wx_app.svg'
import svg_wx_native from '@/assets/svgs/pay/icon/wx_native.svg'
import svg_wx_bar from '@/assets/svgs/pay/icon/wx_bar.svg'
import svg_wallet from '@/assets/svgs/pay/icon/wallet.svg'
import svg_mock from '@/assets/svgs/pay/icon/mock.svg'

export default {
  name: 'PayCashier',
  data() {
    return {
      loading: false, // 支付信息加载状态
      submitLoading: false, // 提交支付状态
      interval: null, // 订单状态定时轮询句柄
      id: undefined, // 支付订单号
      returnUrl: undefined, // 支付完成后的回跳地址
      payOrder: {}, // 支付订单信息对象
      /**
       * 支付宝支付渠道选项
       */
      channelsAlipay: [
        {name: '支付宝 PC 网站支付', icon: svg_alipay_pc, code: 'alipay_pc'},
        {name: '支付宝 Wap 网站支付', icon: svg_alipay_wap, code: 'alipay_wap'},
        {name: '支付宝 App 网站支付', icon: svg_alipay_app, code: 'alipay_app'},
        {name: '支付宝扫码支付', icon: svg_alipay_qr, code: 'alipay_qr'},
        {name: '支付宝条码支付', icon: svg_alipay_bar, code: 'alipay_bar'}
      ],
      /**
       * 微信支付渠道选项
       */
      channelsWechat: [
        {name: '微信公众号支付', icon: svg_wx_pub, code: 'wx_pub'},
        {name: '微信小程序支付', icon: svg_wx_lite, code: 'wx_lite'},
        {name: '微信 App 支付', icon: svg_wx_app, code: 'wx_app'},
        {name: '微信扫码支付', icon: svg_wx_native, code: 'wx_native'},
        {name: '微信条码支付', icon: svg_wx_bar, code: 'wx_bar'}
      ],
      /**
       * 其他支付渠道选项
       */
      channelsMock: [
        {name: '钱包支付', icon: svg_wallet, code: 'wallet'},
        {name: '模拟支付', icon: svg_mock, code: 'mock'}
      ],
      /**
       * 条码支付弹窗数据
       */
      barCode: {
        channelCode: '', // 支付渠道编码
        value: '', // 条形码内容
        title: '', // 弹窗标题
        visible: false // 弹窗可见性
      }
    }
  },
  computed: {
    /**
     * 获取表单实例
     * @returns {Object} 表单对象
     */
    barCodeForm() {
      return this.$form.createForm(this, {name: 'barCodeForm'})
    }
  },
  methods: {
    /**
     * 获取支付订单详情
     * 若未传递id参数或订单不存在，则自动跳转回调页面
     * 若订单已支付/关闭，则提示并跳转
     * @returns {Promise<void>}
     */
    async getDetail() {
      if (!this.orderNo) {
        this.$message.error('未传递支付单号，无法查看对应的支付信息')
        this.goReturnUrl('cancel')
        return
      }
      const data = await PayOrderApi.byOrderNo(this.orderNo, true)
      this.payOrder = data || {}
      if (!data) {
        this.$message.error('支付订单不存在，请检查！')
        this.goReturnUrl('cancel')
        return
      }
      if (data.status === PayStatusEnum.SUCCESS.key) {
        this.$message.success('支付成功')
        this.goReturnUrl('success')
      } else if (data.status === PayStatusEnum.CLOSED.key) {
        this.$message.error('无法支付，原因：订单已关闭')
        this.goReturnUrl('close')
      }
    },
    /**
     * 支付渠道点击处理
     * 条码类弹窗支付特殊处理，公众号/小程序支付不支持PC端
     * 其他渠道直接提交订单
     * @param {string} channelCode 支付渠道编码
     */
    submit(channelCode) {
      if (channelCode === PayChannelCodeEnum.ALIPAY_BAR.key) {
        // 支付宝条码支付，弹窗输入条码
        this.barCode = {
          channelCode: channelCode,
          value: '',
          title: '“支付宝”条码支付',
          visible: true
        }
        return
      }
      if (channelCode === PayChannelCodeEnum.WX_BAR.key) {
        // 微信条码支付，弹窗输入条码
        this.barCode = {
          channelCode: channelCode,
          value: '',
          title: '“微信”条码支付',
          visible: true
        }
        return
      }
      if (channelCode === PayChannelCodeEnum.WX_PUB.key) {
        this.$message.error('微信公众号支付：不支持 PC 网站')
        return
      }
      if (channelCode === PayChannelCodeEnum.WX_LITE.key) {
        this.$message.error('微信小程序：不支持 PC 网站')
        return
      }
      // 其他渠道直接提交
      this.submit0(channelCode)
    },
    /**
     * 提交支付订单并处理不同的支付方式展示
     * @param {string} channelCode 支付渠道编码
     * @returns {Promise<void>}
     */
    async submit0(channelCode) {
      this.submitLoading = true
      try {
        const formData = {
          orderNo: this.orderNo,
          channelCode: channelCode,
          returnUrl: window.location.href,
          ...this.buildSubmitParam(channelCode)
        }
        const data = await PayOrderApi.submitOrder(formData)
        // 若已支付成功，直接提示并跳转
        if (data.status === PayStatusEnum.SUCCESS.key) {
          this.clearQueryInterval()
          this.$message.success('支付成功！')
          this.goReturnUrl('success')
          return
        }
        // 根据支付方式不同做不同展示
        if (data.displayMode === PayDisplayModeEnum.URL.key) {
          this.displayUrl(channelCode, data)
        } else if (data.displayMode === PayDisplayModeEnum.QR_CODE.key) {
          this.displayQrCode(channelCode, data)
        } else if (data.displayMode === PayDisplayModeEnum.APP.key) {
          this.displayApp(channelCode)
        }
        // 开启订单状态轮询
        this.createQueryInterval()
      } finally {
        this.submitLoading = false
      }
    },
    /**
     * 构建提交支付参数（如条码支付需带入条码值）
     * @param {string} channelCode 支付渠道编码
     * @returns {Object} 提交参数
     */
    buildSubmitParam(channelCode) {
      if (channelCode === PayChannelCodeEnum.ALIPAY_BAR.key) {
        // 支付宝条码支付
        return {
          channelExtras: {
            auth_code: this.barCode.value
          }
        }
      }
      if (channelCode === PayChannelCodeEnum.WX_BAR.key) {
        // 微信条码支付
        return {
          channelExtras: {
            authCode: this.barCode.value
          }
        }
      }
      return {}
    },
    /**
     * 跳转类支付方式直接跳转目标链接
     * @param {string} _channelCode 支付渠道编码
     * @param {Object} data 返回数据
     */
    displayUrl(_channelCode, data) {
      window.location.href = data.displayContent
      this.submitLoading = false
    },
    /**
     * 二维码支付方式弹窗展示二维码
     * @param {string} channelCode 支付渠道编码
     * @param {Object} data 返回数据
     */
    displayQrCode(channelCode, data) {
      let title = '请使用手机浏览器“扫一扫”'
      if (channelCode === PayChannelCodeEnum.ALIPAY_WAP.key) {
        // WAP 测试
      } else if (channelCode.indexOf('alipay_') === 0) {
        title = '请使用支付宝“扫一扫”扫码支付'
      } else if (channelCode.indexOf('wx_') === 0) {
        title = '请使用微信“扫一扫”扫码支付'
      }
      // 此处可扩展二维码弹窗功能
      // this.qrCode = {
      //   title: title,
      //   url: data.displayContent,
      //   visible: true
      // }
      this.submitLoading = false
    },
    /**
     * APP类支付方式无法在网页直接发起，给予提示
     * @param {string} channelCode 支付渠道编码
     */
    displayApp(channelCode) {
      if (channelCode === PayChannelCodeEnum.ALIPAY_APP.key) {
        this.$message.error('支付宝 App 支付：无法在网页支付！')
      }
      if (channelCode === PayChannelCodeEnum.WX_APP.key) {
        this.$message.error('微信 App 支付：无法在网页支付！')
      }
      this.submitLoading = false
    },
    /**
     * 创建订单状态轮询，2秒刷新一次
     * 支付完成/关闭后自动停止轮询
     */
    createQueryInterval() {
      if (this.interval) {
        return
      }
      this.interval = setInterval(async () => {
        // 定时查询支付结果
        const data = await PayOrderApi.byOrderNo(this.orderNo)
        if (data.status === PayStatusEnum.SUCCESS.key) {
          this.clearQueryInterval()
          this.$message.success('支付成功！')
          this.goReturnUrl('success')
        }
        if (data.status === PayStatusEnum.CLOSED.key) {
          this.clearQueryInterval()
          this.$message.error('支付已关闭！')
          this.goReturnUrl('close')
        }
      }, 2000)
    },
    /**
     * 清除订单状态轮询
     */
    clearQueryInterval() {
      clearInterval(this.interval)
      this.interval = null
    },
    /**
     * 跳转回调地址，附带支付结果参数
     * @param {string} payResult 支付结果（success/cancel/close）
     */
    goReturnUrl(payResult) {
      this.clearQueryInterval()
      if (!this.returnUrl) {
        return
      }
      const url = this.returnUrl.indexOf('?') >= 0
          ? this.returnUrl + '&payResult=' + payResult
          : this.returnUrl + '?payResult=' + payResult
      if (this.returnUrl.indexOf('http') === 0) {
        window.location.href = url
      } else {
        this.$router.push({path: url})
      }
    }
  },
  /**
   * 组件挂载后初始化支付订单号与回跳地址，并加载支付信息
   */
  mounted() {
    this.orderNo = this.$route.query.orderNo
    if (this.$route.query.returnUrl) {
      this.returnUrl = decodeURIComponent(this.$route.query.returnUrl)
    }
    this.getDetail()
  }
}
</script>

<style lang="less" scoped>
/**
 * 支付渠道选择面板样式
 */
.pay-channel-container {
  display: flex;
  margin-top: -10px;

  .box {
    width: 160px;
    padding-top: 10px;
    padding-bottom: 5px;
    margin-right: 10px;
    text-align: center;
    cursor: pointer;
    border: 1px solid #e6ebf5;

    img {
      width: 40px;
      height: 40px;
    }

    .title {
      padding-top: 5px;
    }
  }
}
</style>